package com.kamistoat.meimeichat.meimeichatclient.kcp;

import com.alibaba.fastjson.JSON;
import com.kamistoat.meimeichat.meimeichatclient.application.UIServiceConnector;
import com.kamistoat.meimeichat.meimeichatclient.config.NettyClientConstant;
import com.kamistoat.meimeichat.meimeichatclient.infrastructure.util.BeanUtil;
import com.kamistoat.meimeichat.meimeichatclient.infrastructure.util.CacheUtil;
import com.kamistoat.meimeichat.meimeichatui.view.chat.IChatMethod;
import com.kamistoat.meimeichatserveraggrement.protocol.Command;
import com.kamistoat.meimeichatserveraggrement.protocol.Packet;
import com.kamistoat.meimeichatserveraggrement.protocol.login.dto.ChatRecordDto;
import com.kamistoat.meimeichatserveraggrement.protocol.p2p.UDP_AnswerP2PClient2Client;
import com.kamistoat.meimeichatserveraggrement.protocol.p2p.UDP_EstablishP2PClient2Server;
import com.kamistoat.meimeichatserveraggrement.protocol.p2p.UDP_MessageClient2Client;
import com.kamistoat.meimeichatserveraggrement.util.R;
import com.kamistoat.meimeichatserveraggrement.util.SerializationUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import javafx.application.Platform;
import org.apache.http.HttpEntity;
import org.apache.http.ParseException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URISyntaxException;
import java.util.Date;
import java.util.List;


public class P2PDatagramHandler extends SimpleChannelInboundHandler<DatagramPacket> {

    private final Logger logger = LoggerFactory.getLogger(P2PDatagramHandler.class);

    protected UIServiceConnector uiServiceConnector;

    public P2PDatagramHandler(UIServiceConnector uiServiceConnector) {
        this.uiServiceConnector = uiServiceConnector;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
        InetSocketAddress fromAddress = packet.sender();
        ByteBuf buf = (ByteBuf) packet.copy().content();

        // 因为前4位是数据长度
        if (buf.readableBytes() < 4) {
            logger.error("收到来自 {} 的UDP链接，但无法解析内容", fromAddress);
            return;
        }
        buf.markReaderIndex();
        int dataLength = buf.readInt();
        if (buf.readableBytes() < dataLength) {
            buf.resetReaderIndex();
            return;
        }

        // 读取UDP报文中首位标志位，判断消息对象
        byte command = buf.readByte();
        // 读取剩下的内容，跳过首位对象标识符
        byte[] req = new byte[dataLength - 1];
        buf.readBytes(req);

        /**
         * 普通String信息
         */
        if (command == Command.NormalString) {
            String notice = new String(req, "UTF-8");
            logger.info(notice);
        }
        /**
         * UDP_AnswerP2PClient2Client 信息
         */
        else if (command == Command.UDP_AnswerP2PClient2Client) {
            UDP_AnswerP2PClient2Client udp_answer =
                    (UDP_AnswerP2PClient2Client) SerializationUtil.deserialize(req, Packet.get(command));
            // 收到半确认应答
            if (udp_answer.getConfirmStatus().equals(NettyClientConstant.UDP_ANSWER_CONFIRM_STATUS.Half_Confirm.getCode())) {
                CacheUtil.p2pMap.put(udp_answer.getTargetUserId(), fromAddress);
                CacheUtil.p2pDateMap.put(udp_answer.getTargetUserId(), new Date());

                logger.info("收到好友 {} 的P2P半确认应答，双向P2P连接已建立完成，并向好友回传全确认应答", udp_answer.getTargetUserId());

                Channel clientP2PChannel = BeanUtil.getBean("ClientP2PChannel", Channel.class);
                UDP_AnswerP2PClient2Client udp_answerP2PClient2Client =
                        new UDP_AnswerP2PClient2Client(CacheUtil.userId, NettyClientConstant.UDP_ANSWER_CONFIRM_STATUS.Full_Confirm.getCode());
                sendUDPAnswerC2C(fromAddress, clientP2PChannel, udp_answerP2PClient2Client);
            }
            // 收到全确认应答
            else if (udp_answer.getConfirmStatus().equals(NettyClientConstant.UDP_ANSWER_CONFIRM_STATUS.Full_Confirm.getCode())) {
                CacheUtil.p2pMap.put(udp_answer.getTargetUserId(), fromAddress);
                CacheUtil.p2pDateMap.put(udp_answer.getTargetUserId(), new Date());
                logger.info("收到好友 {} 的P2P全确认应答，双向P2P连接已建立完成", udp_answer.getTargetUserId());
            }
        }
        /**
         * 是私聊消息
         */
        else if (command == Command.UDP_MessageClient2Client) {
            UDP_MessageClient2Client udpMessage =
                    (UDP_MessageClient2Client) SerializationUtil.deserialize(req, Packet.get(command));
            logger.info("收到好友 {} 通过P2P发送的消息", udpMessage.getSenderNickname());
            // 更新P2P时间
            CacheUtil.p2pDateMap.put(udpMessage.getSenderId(), new Date());
            IChatMethod chat = uiServiceConnector.getIChatMethod();
            Platform.runLater(() -> {
                // 若当前并未与消息发送者建立对话框，则先展开其对话框
                if (!CacheUtil.talkMap.containsKey(udpMessage.getSenderId())) {
                    // 先展开对话框
                    chat.addTalkBox(0, NettyClientConstant.TalkType.Friend.getCode(),
                            udpMessage.getSenderId(), udpMessage.getSenderNickname(), udpMessage.getSenderHead(),
                            "", udpMessage.getMsgDate(), false);
                    CacheUtil.talkMap.put(udpMessage.getSenderId(), 1);
                    // 发送web请求查询两者的旧聊天记录
                    CloseableHttpClient httpClient = null;
                    CloseableHttpResponse response = null;
                    try {
                        httpClient = HttpClientBuilder.create().build();
                        URIBuilder uriBuilder = new URIBuilder(
                                NettyClientConstant.NETTY_SERVER_WEB_HOST +
                                        ":" +
                                        NettyClientConstant.NETTY_SERVER_WEB_PORT +
                                        NettyClientConstant.WebApi.QUERY_FRIEND_CHAT_RECORD.getUri());
                        uriBuilder.addParameter("userId", udpMessage.getReceiverId());
                        uriBuilder.addParameter("talkId", udpMessage.getSenderId());
                        uriBuilder.addParameter("msgDate", String.valueOf(udpMessage.getMsgDate().getTime()));
                        HttpGet httpGet = new HttpGet(uriBuilder.build());
                        response = httpClient.execute(httpGet);
                        HttpEntity responseEntity = response.getEntity();
                        if (responseEntity != null) {
                            R responseR = JSON.parseObject(EntityUtils.toString(responseEntity), R.class);
                            if ((int) responseR.get("code") == 0 && JSON.parseArray(JSON.toJSONString(responseR.get("chatList")), ChatRecordDto.class).size() > 0) {
                                List<ChatRecordDto> chatRecordDtos = JSON.parseArray(JSON.toJSONString(responseR.get("chatList")), ChatRecordDto.class);
                                // 填充聊天信息
                                for (int i = chatRecordDtos.size() - 1; i >= 0; i--) {
                                    ChatRecordDto chatRecord = chatRecordDtos.get(i);
                                    //  自己的消息
                                    if (NettyClientConstant.MsgUserType.Myself.getCode().equals(chatRecord.getMsgUserType())) {
                                        chat.addTalkMsgRight(chatRecord.getTalkId(), chatRecord.getMsgContent(), chatRecord.getMsgType(), chatRecord.getMsgDate(), true, false, false);
                                    } else {
                                        // 好友的消息
                                        chat.addTalkMsgUserLeft(chatRecord.getTalkId(), null, null, chatRecord.getMsgContent(), chatRecord.getMsgType(), chatRecord.getMsgDate(), true, false, false);
                                    }
                                }

                            }
                        }
                    } catch (ParseException | URISyntaxException | IOException var24) {
                        var24.printStackTrace();
                    } finally {
                        try {
                            if (httpClient != null) {
                                httpClient.close();
                            }

                            if (response != null) {
                                response.close();
                            }
                        } catch (IOException var23) {
                            var23.printStackTrace();
                        }
                    }
                }
                // 寻找消息发送者对应的talkBox，将消息显示出来
                // 可能存在对应的talkBox以收起的情况，则需要展开该talkBox，
                chat.addTalkMsgUserLeft(udpMessage.getSenderId(), udpMessage.getSenderNickname(), udpMessage.getSenderHead(),
                        udpMessage.getMsgText(), udpMessage.getMsgType(), udpMessage.getMsgDate(),
                        true, false, true);
            });
        }
    }

    /**
     * 将udp_answerP2PClient2Client构建成UDP信息进行发送
     *
     * @param targetAddress              目标UDP地址
     * @param clientP2PChannel           当前UDP通道
     * @param udp_answerP2PClient2Client
     */
    public static void sendUDPAnswerC2C(InetSocketAddress targetAddress, Channel clientP2PChannel, UDP_AnswerP2PClient2Client udp_answerP2PClient2Client) {
        byte[] client2ClientMsgBytes = SerializationUtil.serialize(udp_answerP2PClient2Client);
        ByteBuf client2ClientMsgBuf = Unpooled.buffer();
        client2ClientMsgBuf.writeInt(client2ClientMsgBytes.length + 1);
        client2ClientMsgBuf.writeByte(Command.UDP_AnswerP2PClient2Client);
        client2ClientMsgBuf.writeBytes(client2ClientMsgBytes);
        clientP2PChannel.writeAndFlush(
                new DatagramPacket(client2ClientMsgBuf, targetAddress));
    }

    /**
     * 通过P2P向好友发送私聊信息，并更新P2P时间
     *
     * @param clientP2PChannel
     * @param friendAddress
     * @param udp_messageClient2Client
     */
    public static void sendUDPMessage2Client(Channel clientP2PChannel, InetSocketAddress friendAddress, UDP_MessageClient2Client udp_messageClient2Client) {
        byte[] udpMessageBytes = SerializationUtil.serialize(udp_messageClient2Client);
        ByteBuf udpMessageBuf = Unpooled.buffer();
        udpMessageBuf.writeInt(udpMessageBytes.length + 1);
        udpMessageBuf.writeByte(Command.UDP_MessageClient2Client);
        udpMessageBuf.writeBytes(udpMessageBytes);
        clientP2PChannel.writeAndFlush(
                new DatagramPacket(udpMessageBuf, friendAddress));
        CacheUtil.p2pDateMap.put(udp_messageClient2Client.getReceiverId(), new Date());
    }
}
